﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Web;

namespace PageAdmin.Utils
{
    /// <summary>
    /// webaip帮助类
    /// </summary>
    public class WebApiHelper
    {
        /// <summary>
        /// 单位秒,请求超时时间
        /// </summary>
        public int TimeoutSecond { set; private get; } = 5000;

        /// <summary>
        /// 传递的Cookie
        /// </summary>
        public Dictionary<string, string> CookieDictionary { set; get; }
        

        /// <summary>
        /// 请求头参数字典
        /// </summary>
        public Dictionary<string, string> HeaderDictionary { set; get; }

        /// <summary>
        /// 是否自动重定向
        /// </summary>
        public bool AllowAutoRedirect { set; get; } = false;

        /// <summary>
        /// 来路,解决防盗链问题
        /// </summary>
        public string Referer { set; get; }

        /// <summary>
        /// 请求api
        /// </summary>
        private string _api;
        public string Api
        {
            set
            {
                _api = value;
                if (string.IsNullOrEmpty(Api))
                {
                    _api = "/e/api/";
                }
                if (!_api.StartsWith("http://", StringComparison.OrdinalIgnoreCase) && !_api.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
                {
                    if (!string.IsNullOrEmpty(systemConfig.IntranetAddress))
                    {
                        _api = systemConfig.IntranetAddress + _api;
                    }
                    else
                    {
                        _api = systemConfig.Domain + _api;
                    }
                }
            }
            get
            {
                return _api;
            }
        }

        private string _AccessKeyId;

        private string _AccessKeySecret;

        private SystemConfig systemConfig = SystemConfig.Get();
        /// <summary>
        /// 根据配置文件实例化，依赖主站下的/Config/System.config文件
        /// </summary>
        public WebApiHelper()
        {
            Api = "/E/Api/";//系统默认api
            _AccessKeyId = systemConfig.AccessKeyId;
            _AccessKeySecret = systemConfig.AccessKeySecret;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="api">自定义api，api中不要带任何参数</param>
        public WebApiHelper(string api)
        {
            Api = api;
            if (string.IsNullOrEmpty(api))
            {
                Api = "/E/Api";
            }
            _AccessKeyId = systemConfig.AccessKeyId;
            _AccessKeySecret = systemConfig.AccessKeySecret;
        }
        /// <summary>
        /// 自定义参数进行实例化
        /// </summary>
        /// <param name="accessKeyId"></param>
        /// <param name="accessKeySecret"></param>
        public WebApiHelper(string accessKeyId, string accessKeySecret)
        {
            Api = "/E/Api";
            _AccessKeyId = accessKeyId;
            _AccessKeySecret = accessKeySecret;
        }

        /// <summary>
        /// 完全自定义参数进行实例化
        /// </summary>
        /// <param name="api">不需要加任何参数，需要带http://或https://前缀</param>
        /// <param name="accessKeyId"></param>
        /// <param name="accessKeySecret"></param>
        public WebApiHelper(string api, string accessKeyId, string accessKeySecret)
        {
            Api = api;
            _AccessKeyId = accessKeyId;
            _AccessKeySecret = accessKeySecret;
        }

        /// <summary>
        /// Post请求
        /// </summary>
        /// <returns></returns>
        public string Post()
        {
            Dictionary<string, string> dic = new Dictionary<string, string>();
            return Post(dic);
        }

        /// <summary>
        /// Post请求
        /// </summary>
        /// <param name="formData"></param>
        /// <returns></returns>
        public string Post(NameValueCollection formData)
        {
            Dictionary<string, string> dic = IEnumerableHelper.FormatDictionary(formData);
            return Post(dic);
        }

        /// <summary>
        /// Post请求
        /// </summary>
        /// <param name="postData">form参数</param>
        /// <returns></returns>
        public string Post(Dictionary<string, string> postData)
        {
            if (postData == null)
            {
                postData = new Dictionary<string, string>();
            }
            string timeStamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss");
            string signatureNonce = Guid.NewGuid().ToString("N");
            string query = GetQueryString(postData).Item2;
            string api = Api;
            Dictionary<string, string> headerParams = new Dictionary<string, string>();
            //加入头信息
            headerParams.Add("AccessKeyId", _AccessKeyId);
            headerParams.Add("SignatureNonce", signatureNonce); //用于请求的防重放攻击，每次请求唯一
            headerParams.Add("Timestamp", timeStamp); //发起请求时的时间戳（单位：毫秒）
            headerParams.Add("Signature", GetSignature(_AccessKeyId, _AccessKeySecret, query, signatureNonce, timeStamp)); //当前请求内容的数字签名
            if (HeaderDictionary != null)
            {
                foreach (KeyValuePair<string, string> keyValue in HeaderDictionary)
                {
                    headerParams.Add(keyValue.Key, keyValue.Value);
                }
            }
            //添加登录令牌
            if (!DictionaryHelper.ContainsKeyIgnoreCase(headerParams, "UserToken"))
            {
                var request = System.Web.HttpContext.Current?.Request;
                if (request != null)
                {
                    string userToken = request.Cookies["UserToken"]?.Value;
                    if (string.IsNullOrEmpty(userToken))
                    {
                        userToken = request.Headers["UserToken"];
                    }
                    headerParams.Add("UserToken", userToken);
                }
            }
            HttpRequestHelper httpRequestHelper = new HttpRequestHelper();
            httpRequestHelper.Referer = Referer;
            httpRequestHelper.TimeoutSecond = TimeoutSecond;
            httpRequestHelper.HeaderDictionary = headerParams;
            httpRequestHelper.CookieDictionary = this.CookieDictionary;
            httpRequestHelper.AllowAutoRedirect = this.AllowAutoRedirect;
            return httpRequestHelper.Post(api, postData);
        }


        /// <summary>
        /// get请求
        /// </summary>
        /// <returns></returns>
        public string Get()
        {
            return Get(null);
        }
        /// <summary>
        /// Get请求
        /// </summary>
        /// <param name="queryString">url参数字典</param>
        public string Get(Dictionary<string, string> queryString)
        {
            if (queryString == null)
            {
                queryString = new Dictionary<string, string>();
            }
            Tuple<string, string> tuple = GetQueryString(queryString);
            string api = Api;
            if (api.IndexOf("?") < 0)
            {
                api += "?" + tuple.Item1;
            }
            else
            {
                api += "&" + tuple.Item1;
            }
            string timeStamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss");
            string signatureNonce = Guid.NewGuid().ToString("N");

            Dictionary<string, string> headerParams = new Dictionary<string, string>();
            //加入头信息
            headerParams.Add("AccessKeyId", _AccessKeyId);
            headerParams.Add("SignatureNonce", signatureNonce); //用于请求的防重放攻击，每次请求唯一
            headerParams.Add("Timestamp", timeStamp); //发起请求时的时间戳（单位：毫秒）
            headerParams.Add("Signature", GetSignature(_AccessKeyId, _AccessKeySecret, tuple.Item2, signatureNonce, timeStamp)); //当前请求内容的数字签名
            if (HeaderDictionary != null)
            {
                foreach (KeyValuePair<string, string> keyValue in HeaderDictionary)
                {
                    headerParams.Add(keyValue.Key, keyValue.Value);
                }
            }
            //添加登录令牌
            if (!DictionaryHelper.ContainsKeyIgnoreCase(headerParams, "UserToken"))
            {
                var request = System.Web.HttpContext.Current?.Request;
                if (request != null)
                {
                    string userToken = request.Cookies["UserToken"]?.Value;
                    if (string.IsNullOrEmpty(userToken))
                    {
                        userToken = request.Headers["UserToken"];
                    }
                    headerParams.Add("UserToken", userToken);
                }
            }
            HttpRequestHelper httpRequestHelper = new HttpRequestHelper();
            httpRequestHelper.Referer = Referer;
            httpRequestHelper.TimeoutSecond = TimeoutSecond;
            httpRequestHelper.HeaderDictionary = headerParams;
            httpRequestHelper.CookieDictionary = this.CookieDictionary;
            httpRequestHelper.AllowAutoRedirect = this.AllowAutoRedirect;
            return httpRequestHelper.Get(api);
        }


        /// <summary>
        /// 计算签名
        /// </summary>
        /// <param name="accessKeyId"></param>
        /// <param name="accessKeySecret"></param>
        /// <param name="query"></param>
        /// <param name="signatureNonce"></param>
        /// <param name="timeStamp"></param>
        /// <returns></returns>
        private static string GetSignature(string accessKeyId, string accessKeySecret, string query, string signatureNonce, string timeStamp)
        {
            if (query == null)
            {
                query = "";
            }
            query = $"AccessKeyId{accessKeyId}SignatureNonce={signatureNonce}TimeStamp{timeStamp}{query}";
            ISigner signer = new HmacSha1();
            return signer.SignString(query.ToLower(), accessKeySecret);
        }


        /// <summary>
        /// 检测签名是否正确
        /// </summary>
        /// <param name="requestBase"></param>
        /// <param name="accessKeySecret"></param>
        /// <param name="timestampInterval"></param>
        /// <returns></returns>
        public static TipsInfo CheckSignature(HttpRequestBase request, string accessKeySecret, int timestampInterval = 5)
        {
            TipsInfo tipsInfo = new TipsInfo();
            if (request == null)
            {
                tipsInfo.State = 0;
                tipsInfo.Msg = "request is null";
                return tipsInfo;
            }
            string accessKeyId = request.Headers["AccessKeyId"];
            string signatureNonce = request.Headers["SignatureNonce"];
            string timestamp = request.Headers["Timestamp"];
            string signature = request.Headers["Signature"];
            if (string.IsNullOrEmpty(accessKeyId))
            {
                tipsInfo.State = 0;
                tipsInfo.Msg = "AccessKeyId in the request header has not set";
                return tipsInfo;
            }
            if (!StringHelper.IsDateTime(timestamp))
            {
                tipsInfo.State = 0;
                tipsInfo.Msg = "Timestamp in the request header is error, Timestamp must be the GMT time and format is yyyy-MM-dd HH:mm:ss";
                return tipsInfo;
            }
            if (string.IsNullOrEmpty(signatureNonce))
            {
                tipsInfo.State = 0;
                tipsInfo.Msg = "SignatureNonce in the request header is not set";
                return tipsInfo;
            }
            if (string.IsNullOrEmpty(signature))
            {
                tipsInfo.State = 0;
                tipsInfo.Msg = "Signature in the request header is not set";
                return tipsInfo;
            }
            if (string.IsNullOrEmpty(accessKeySecret))
            {
                tipsInfo.State = 0;
                tipsInfo.Msg = "AccessKeyId is error, no matching AccessKeySecret";
                return tipsInfo;
            }

            //检测时间戳
            DateTime dtNow = DateTime.Parse(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"));
            TimeSpan timeSpan = dtNow - DateTime.Parse(timestamp);
            if (timestampInterval < 0)
            {
                timestampInterval = 5;
            }
            if (timeSpan.TotalMinutes > timestampInterval || timeSpan.TotalMinutes < (-1*timestampInterval))
            {
                tipsInfo.State = 0;
                tipsInfo.Msg = $"Timestamp has exceeded the allowed time range";
                return tipsInfo;
            }
            ///检测重放
            string cacheKey = "WebApiSignatureNonce_" + signature;
            if (CacheHelper.Get(cacheKey) == null)
            {
                CacheHelper.Add(cacheKey, "", timestampInterval);
            }
            else
            {
                tipsInfo.State = 0;
                tipsInfo.Msg = "SignatureNonce already exists, do not repeat the request";
                return tipsInfo;
            }
            var method = request.HttpMethod;
            string queryParams = null;
            Dictionary<string, string> paramsDic = new Dictionary<string, string>();
            if (method.Equals("post", StringComparison.OrdinalIgnoreCase))
            {
               var formData =request.Unvalidated.Form;
                foreach (string key in formData.AllKeys)
                {
                    if (!string.IsNullOrEmpty(key))
                    {
                        paramsDic.Add(key, formData[key]);
                    }
                }
                queryParams = GetQueryString(paramsDic).Item2;
            }
            else
            {
                var queryString = request.QueryString;
                foreach (string key in queryString.AllKeys)
                {
                    if (!string.IsNullOrEmpty(key))
                    {
                        paramsDic.Add(key, queryString[key]);
                    }
                }
                queryParams = GetQueryString(paramsDic).Item2;
            }
            string signatureString = GetSignature(accessKeyId, accessKeySecret, queryParams, signatureNonce, timestamp);
            if (!signatureString.Equals(signature))
            {
                tipsInfo.State = 0;
                tipsInfo.Msg = "invalid signature";
                return tipsInfo;
            }
            return tipsInfo;
        }

        /// <summary>
        /// 拼接参数，方便加密
        /// </summary>
        /// <param name="parames"></param>
        /// <returns></returns>
        private static Tuple<string, string> GetQueryString(Dictionary<string, string> parames)
        {
            if (parames == null)
            {
                return new Tuple<string, string>("", "");
            }
            else if (parames.Count == 0)
            {
                return new Tuple<string, string>("", "");
            }

            // 第一步：把字典按Key的字母顺序排序
            IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parames);
            IEnumerator<KeyValuePair<string, string>> dem = sortedParams.GetEnumerator();

            // 第二步：把所有参数名和参数值串在一起
            StringBuilder query = new StringBuilder("");  //签名字符串,格式:a=1&b=2
            StringBuilder query1 = new StringBuilder("");  //签名字符串,格式:a1b2
            while (dem.MoveNext())
            {
                string key = dem.Current.Key;
                string value = dem.Current.Value;
                if (!string.IsNullOrEmpty(key))
                {
                    query.Append("&").Append(UrlEncode(key)).Append("=").Append(UrlEncode(value));
                    query1.Append(key).Append(value);
                }
            }
            return new Tuple<string, string>(query.ToString().Substring(1), query1.ToString());
        }


        private static String UrlEncode(string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                return null;
            }
            else
            {
                return HttpUtility.UrlEncode(value, Encoding.UTF8);
            }
        }
    }

}
